/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package blueprint4j.gui.db;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.xml.stream.events.EntityDeclaration;

import blueprint4j.db.DBConnection;
import blueprint4j.db.DataException;
import blueprint4j.db.Entity;
import blueprint4j.db.EntityChild;
import blueprint4j.db.Field;
import blueprint4j.db.FieldForeignKeyInterface;
import blueprint4j.db.FieldGroup;
import blueprint4j.db.FieldString;
import blueprint4j.db.FieldText;
import blueprint4j.db.VectorField;
import blueprint4j.gui.Binder;
import blueprint4j.gui.DataPanelInterface;
import blueprint4j.gui.TabbedpaneGroup;
import blueprint4j.utils.BindException;
import blueprint4j.utils.BindFieldInterface;
import blueprint4j.utils.Bindable;
import blueprint4j.utils.Log;


/**
 * This class represents a editable entity panel
 * With a Panel List on the Left
 * Binding to DataPanels on the right
 */
public class Editor extends JPanel implements
        DataPanelInterface,
        TabbedpaneGroup.TabSelectedInterface ,
        EntityPanelNewEventListner,
        EntityPanelSelectionListner,
        EntityEditorSavedEventListner {
    
	private static final long serialVersionUID = 1L;
	private DBConnection dbcon = null;
    private Entity entity = null;
    private EntityChild child = null;
    //private Editor editables [] = new Editor[0];
    private Vector<Editor> v_editor = new Vector<Editor>();
    private String name = null;
    private EntityEditor entity_editor = null;
    //private boolean other_where_set = false;
    protected Editor parent_edit = null;
    // THIS IS ONLY WHEN THE FIRST LEVEL IS A CHILD INSTANCE
    private Entity parent_entity = null;
    private EntityWizard entity_wizard = null;
    private EntityPanelDeleteAuthorizer deleteAuthorizer = null;
    //private DataPanelInterface[] panels=null;
    private VectorDataPanelInterface v_panel = new VectorDataPanelInterface();
    private String list_position= null;
    private String other_where = null;
    private boolean default_add=true;
    private boolean default_delete=true;
    private boolean default_import=true;
    private boolean default_export=true;
    private List<EntityPanelNewEventListner> new_listeners=new ArrayList<EntityPanelNewEventListner>();
    
    private FieldGroup entityFieldGroup[] = null;
    
    private Dimension preferred_size = null;
    
    private Integer permission = null;
    
    private int tab_placement=EntityEditor.default_tab_placement;
    
    /*
    private Editor(DBConnection dbcon,String name,Entity entity,EntityChild child,VectorEditor v_editor,VectorDataPanelInterface v_panel,String position)
    throws BindException,IOException {
        this.dbcon = dbcon;
        this.name = name;
        this.entity =entity;
        this.child = child;
        this.v_editor = v_editor;
        this.v_panel = v_panel;
    }*/
    
    public void setPermission(int permission) {
        this.permission = new Integer(permission);
    }
    
    protected Editor(DBConnection dbcon,String name,String position)
    throws BindException,IOException {
        this.dbcon = dbcon;
        this.name = name;
        this.list_position = position;
        setLayout(new BorderLayout());
    }
    
    public Editor(DBConnection dbcon,String name,Entity entity,String position)
    throws BindException,IOException {
        this(dbcon,name,position);
        this.entity = entity;
        buildEditor();
    }
    
    public Editor(DBConnection dbcon,String name,EntityChild child,String position)
    throws BindException,IOException {
        this(dbcon,name,position);
        this.child = child;
        buildEditor();
    }
    
    public Editor(DBConnection dbcon,String name,Entity entity)
    throws BindException,IOException {
        this(dbcon,name,entity,EntityEditor.LEFT);
    }
    
    public Editor(DBConnection dbcon,String name,EntityChild child)
    throws BindException,IOException {
        this(dbcon,name,child,EntityEditor.LEFT);
    }
    
    public Editor(DBConnection dbcon,String name,Entity entity,Editor editables[],String position)
    throws BindException,IOException {
        this(dbcon,name,entity,position);
        this.v_editor = new Vector<Editor>();
        addEditors(editables);
        buildEditor();
    }
    
    public Editor(DBConnection dbcon,String name,EntityChild child,Editor editables[],String position)
    throws BindException,IOException {
        this(dbcon,name,child,position);
        this.v_editor = new Vector<Editor>();
        addEditors(editables);
        buildEditor();
    }
    
    public Editor(DBConnection dbcon,String name,Entity entity,Editor editables[])
    throws BindException,IOException {
        this(dbcon,name,entity,editables,EntityEditor.LEFT);
    }
    
    public Editor(DBConnection dbcon,String name,EntityChild child,Editor editables[])
    throws BindException,IOException {
        this(dbcon,name,child,editables,EntityEditor.LEFT);
    }
    
    public void refreshList(){
        entity_editor.refreshList();
    }
    
    public void setFieldGroup(FieldGroup [] entityFieldGroup) {
        this.entityFieldGroup = entityFieldGroup;
    }
    
    public void addEditors(Editor[] editors)
    throws BindException,IOException {
    	for (Editor e:editors){
    		addEditor(e);
    	}
    }

    public void addEditor(Editor editor)
    throws BindException,IOException {
        editor.buildEditor();
        v_editor.add(editor);
    }
       
    public void setEntityAuthorizer(EntityPanelDeleteAuthorizer deleteAuthorizer) {
        this.deleteAuthorizer = deleteAuthorizer;
        if (entity_editor != null) {
            entity_editor.setAuthorizer(deleteAuthorizer);
        }
    }
    
    public void setEntityWizard(EntityWizard entity_wizard) {
        this.entity_wizard = entity_wizard;
        if (entity_editor != null) {
            entity_editor.setEntityWizard(entity_wizard);
        }
    }
    
    public void addDataPanel(DataPanelInterface panel) {
        v_panel.add(panel);
    }
    
    public void setParent(Entity parent)
    throws BindException {
        child.rebind(parent);
        entity = child.getChild(true);
    }
    
    public void setOtherWhere(String other_where) {
        this.other_where = other_where;
        try {
			buildEditor();
		} catch (BindException e) {
			Log.critical.out(e.getMessage(),e);
		} catch (IOException e) {
			Log.critical.out(e.getMessage(),e);
		}
    }
    
    public void buildEditor()
    throws BindException,IOException {
        build(new Binder(),entity);
    }
    
    public void removeReferences() {
        this.removeAll();
        if (entity_editor != null) {
            entity_editor.removeReferences();
            entity_editor.removeNewEventListner(this);
            entity_editor.removeSelectionListner(this);
            entity_editor.setEntityEditorSavedEventListner(null);
            entity_editor.setEntityWizard(null);
        }
    }
    
    public void setListPreferredSize(Dimension preferred_size) {
        this.preferred_size = preferred_size;
        System.out.println("Editor Set Preffed List size 01");
        if (entity_editor != null) {
            System.out.println("Editor Set Preffed List size 02 " + preferred_size);
            entity_editor.setListPreferredSize(preferred_size);
        }
    }
    
    /**
     * Bindable will always be the Parent entity
     */
    public void build(Binder binder,Bindable bindable)
    throws IOException, BindException {
        try {
            // REMOVE ALL LISTENERS FOR GARBAGE COLLECTION
            removeReferences();
            Entity parent = (Entity) bindable;
            if (parent == null) {
                return;
            }
            if (child != null) {
                child.rebind(parent);
                entity = child.getChild(true);
            } else {
                entity = parent;
            }
            // IF THIS IS A FIRST LEVAL AND IT IS A CHILD
            if (child != null && parent_edit == null) {
                parent_entity = (Entity) bindable;
            }
            // ADD ALL OTHER ENTITY EDITABLES
            DataPanelInterface datapanel[] = new DataPanelInterface[v_editor.size()+1+v_panel.size()];
            if (entityFieldGroup != null) {
                datapanel[0] = new DataPanelEntity(name,entityFieldGroup ,null);
            } else {
                datapanel[0] = new DataPanelEntity(name,entity);
            }
            
            entity_editor = new EntityEditor(dbcon,name);
            entity_editor.setTabPlacement(tab_placement);

            if (entity != null) {
                if (permission != null) {
                    entity.setEntityType(permission.intValue());
                }
            }
            
            entity_editor.setListLocation(list_position);
            entity_editor.setEntityWizard(entity_wizard);
            //entity_editor.setParentBinder(binder);
            
            for (int i = 1;i <= v_editor.size();i++) {
                v_editor.get(i-1).parent_edit = this;
                v_editor.get(i-1).setBinderBindable(binder,null);
                //v_editor.get(i-1).build(binder,null);
                datapanel[i] = v_editor.get(i-1);
            }
            for (int i=0;i<v_panel.size();i++) {
                datapanel[i+1+v_editor.size()] = v_panel.get(i);
                v_panel.get(i).build(binder, bindable);
            }
            if (child != null) {
                entity_editor.setOtherWhere(child.getSqlWhere()+(other_where==null?"":(" "+other_where)));
            } else if (other_where != null) {
                entity_editor.setOtherWhere(other_where);
            }
            if (preferred_size != null) {
                entity_editor.setListPreferredSize(preferred_size);
            }
            entity_editor.setDetails(
                    entity,
                    toEntityPanelFieldBinder(entity.getNameFields()),
                    datapanel,
                    200
                    );
            entity_editor.addSelectionListner(this);
            entity_editor.addNewEventListner(this);
            entity_editor.setEntityEditorSavedEventListner(this);
            //
            
            entity_editor.enableAdd(entity.mayCreate());
            entity_editor.enableImport(entity.mayCreate());
            entity_editor.enableSearch(entity.maySearch());
            entity_editor.enableDelete(entity.mayDelete());
            
            add(entity_editor,BorderLayout.CENTER);
                        /*
                        if ((EntityEditor.TOP.equals(list_position) || EntityEditor.BOTTOM.equals(list_position)) && getSize().getWidth() > 0) {
                                entity_editor.getEntityPanel().setPreferredSize(new Dimension((int)getSize().getWidth()-20,100));
                                entity_editor.getEntityPanel().setColumnTotalWidth((int)getSize().getWidth()-20);
                        }*/
            
            entity_editor.enableAdd(getDefaultAdd());
            entity_editor.enableDelete(getDefaultDelete());
            entity_editor.enableImport(getDefaultImport());
            entity_editor.enableExport(getDefaultExport());
            entity_editor.setAuthorizer(deleteAuthorizer);
            
       } catch (Exception exception) {
            throw new BindException(exception);
        }
    }

    private int getFieldLength(Field field) {
        int len = 0;
        if (field instanceof FieldText) {
            len = 480;
        } else if (field instanceof FieldString) {
            len = ((FieldString)field).getLength()*4;
        } else {
            len = 30;
        }
        if (len < new javax.swing.JLabel(field.getDescription()).getMinimumSize().getWidth()*2) {
            len = (int) new javax.swing.JLabel(field.getDescription()).getMinimumSize().getWidth()*2;
        }
        return len;
    }

    public EntityPanelFieldBinder[] toEntityPanelFieldBinder(VectorField vf) {
    	EntityPanelFieldBinder panels[] = new EntityPanelFieldBinder[vf.size()];
    	for (int i =0;i < vf.size();i++) {
    		panels[i] =
    			new DefaultFieldBinder(
    					vf.get(i),
    					getFieldLength(vf.get(i))
    			);
    	}
    	return panels;
    }

    
    public void addNewEventListner(EntityPanelNewEventListner listner) {
        //entity_editor.addNewEventListner(listner);
    	new_listeners.add(listner);
    }
    
    public void addSelectionListner(EntityPanelSelectionListner listner) {
        entity_editor.addSelectionListner(listner);
    }
    
    public String getName() {
        return name;
    }
    
    public void selection(Entity entity) {
        try {
            if (entity != null) {
                this.entity = entity;
                for (int i =0;i < v_editor.size();i++) {
                    if (v_editor.get(i).entity_editor == null) {
                        v_editor.get(i).build(entity_editor.getBinder(),entity);
                    }
                    v_editor.get(i).entity_editor.setParentBinder(entity_editor.getBinder());
                }
            }
        } catch (Exception exception) {
            Log.debug.out(exception);
        }
    }
    
    public boolean eventNew(Entity p_entity)
    throws DataException, java.sql.SQLException {
        try {
            this.entity = p_entity;
            // rebind with parent
            if (child != null) {
                if (parent_entity == null) {
                    ((FieldForeignKeyInterface)entity.getFieldByName(child.getFieldForeignKey().getName())).setParentEntity(parent_edit.entity);
                } else {
                    ((FieldForeignKeyInterface)entity.getFieldByName(child.getFieldForeignKey().getName())).setParentEntity(parent_entity);
                }
            }
            // Call other listeners.
            for (EntityPanelNewEventListner ne:new_listeners){
            	boolean result=ne.eventNew(p_entity);
            	if (!result){
            		return false;
            	}
            }
            return true;
        } catch (Exception exception) {
            throw new DataException(exception);
        }
    }
    
    public void activated() {
    	for (int t=0;t<v_panel.size();t++){
    		v_panel.get(t).activated();
    	}
    }
    
    public void deactivated() {
    	for (int t=0;t<v_panel.size();t++){
    		v_panel.get(t).deactivated();
    	}
    }
    
    public void editorSaved() {
        try {
        	for (int e=0;e<v_editor.size();e++){
        		v_editor.get(e).save();
        	}
        	for (int t=0;t<v_panel.size();t++){
        		v_panel.get(t).deactivated();
        		v_panel.get(t).activated();
        	}
            if (entity != null && entity.id.getAsObject() != null && child != null && child.wasSqlWhereInvalid()) {
                entity_editor.setOtherWhere(child.getSqlWhere());
            }
        } catch (Exception exception) {
            Log.debug.out(exception);
        }
    }
    
    public EntityEditor getEntityEditor() {
        return entity_editor;
    }
    
    public boolean save() {
        return true;
    }
    
    public void selected() {
    	refreshList();
    }
    
    public void setTabbedpaneGroup(TabbedpaneGroup parent) {}
    
    
    public JComponent getComponent() {
        return this;
    }
    
    public boolean doesPanelContain(BindFieldInterface field) throws BindException {
        return false;
    }
    
    public void setBinderBindable(Binder binder,Bindable bindable) {
        //this.binder = binder;
        this.entity = (Entity) bindable;
        try {
            build(binder,bindable);
            for (int t=0;t<v_panel.size();t++){
            	v_panel.get(t).setBinderBindable(binder,bindable);
            }
        } catch (Exception exception) {
            Log.debug.out(exception);
        }
    }
    
    /**
     * This would set the master entity
     *
     * public void setParent() {
     * master = master;
     * }*/
    
    
    /**
     * Add a new child DataPanel
     * This would build the childs whole field group set
     *
     * public void addChild(Child child) {}*/
    
    /**
     * Add a new Child Datapanel
     * Would only build the specific dataset
     *
     * public void addChild(Child child,FieldGroup[] groups) {}*/
    
    /**
     * Add a new binding set
     * This would call the setEntity method on editable
     *
     * public void addEditableChild(Editor editable) {}
     */
    
    public void setDefaultAdd(boolean default_add) {
		this.default_add = default_add;
		if (entity_editor!=null){
			entity_editor.enableAdd(default_add);
		}
	}

	public boolean getDefaultAdd() {
		return default_add;
	}
	
    public void setTabPlacement(int tab_placement){
    	this.tab_placement=tab_placement;
    	if (entity_editor!=null){
    		entity_editor.setTabPlacement(tab_placement);		
    	}
    }

	public void setDefaultDelete(boolean default_delete) {
		this.default_delete = default_delete;
		if (entity_editor!=null){
			entity_editor.enableDelete(default_delete);
		}
	}

	public boolean getDefaultDelete() {
		return default_delete;
	}

	public void setDefaultImport(boolean default_import) {
		this.default_import = default_import;
		if (entity_editor!=null){
			entity_editor.enableImport(default_import);
		}
	}

	public boolean getDefaultImport() {
		return default_import;
	}

	public void setDefaultExport(boolean default_export) {
		this.default_export = default_export;
		if (entity_editor!=null){
			entity_editor.enableExport(default_export);
		}
	}

	public boolean getDefaultExport() {
		return default_export;
	}

	/**
     * Represents a container of entity and its displayable field groups
     */
    public static class EntityFieldGroup {
        
        public Entity entity = null;
        public FieldGroup [] group = null;
        public EntityChild child = null;
        
        /**
         * This will add all the field groups of the entity
         */
        public EntityFieldGroup(Entity entity) {
            this.entity = entity;
            group = entity.getFieldGroups().toArray();
        }
        
        public EntityFieldGroup(Entity entity,FieldGroup [] group) {
            this.entity = entity;
            this.group = group;
        }
        
        /**
         * This represents an child entity addition
         * Adds the field groups of the child
         */
        public EntityFieldGroup(FieldGroup [] group,EntityChild child) {
            this.entity = child.getChildInstance();
            this.group = group;
            this.child = child;
        }
        
        /**
         * This represents an child entity addition
         * Adds all field groups of the child
         */
        public EntityFieldGroup(EntityChild child) {
            this(child.getChildInstance().getFieldGroups().toArray(),child);
        }
    }
    
    /*
    public static class VectorEditor {
        private Vector<>Editor> store=new Vector();
        
        public VectorEditor() {}
        
        public VectorEditor(Editor editables[]) {
            for (int i =0;i <editables.length;i++) {
                add(editables[i]);
            }
        }
        
        public Editor get(int pos) {
            return (Editor)store.get(pos);
        }
        
        public void add(Editor item) {
            store.add(item);
        }
        
        public boolean remove(Editor item) {
            return store.remove(item);
        }
        
        public Editor remove(int pos) {
            return (Editor) store.remove(pos);
        }
        
        public int size() {
            return store.size();
        }
    }*/
    
    public static class VectorDataPanelInterface {
        private Vector<DataPanelInterface> store=new Vector<DataPanelInterface>();
        
        public VectorDataPanelInterface() {}
        
        public VectorDataPanelInterface(DataPanelInterface panels[]) {
            for (int i =0;i <panels.length;i++) {
                add(panels[i]);
            }
        }
        
        public DataPanelInterface get(int pos) {
            return (DataPanelInterface)store.get(pos);
        }
        
        public void add(Vector<Editor> editables) {
            for (int i = editables.size()-1;i >= 0;i--) {
                add(1,editables.get(i));
            }
        }
        
        public void add(DataPanelInterface item) {
            store.add(item);
        }
        
        public void add(int pos,DataPanelInterface item) {
            store.add(pos,item);
        }
        
        public boolean remove(DataPanelInterface item) {
            return store.remove(item);
        }
        
        public DataPanelInterface remove(int pos) {
            return (DataPanelInterface) store.remove(pos);
        }
        
        public int size() {
            return store.size();
        }
        
        public DataPanelInterface[] toArray() {
            DataPanelInterface panels[]= new DataPanelInterface[size()];
            for (int i = 0;i < size();i++) {
                panels[i] = get(i);
            }
            return panels;
        }
    }

	public void activate() {
    	for (int t=0;t<v_panel.size();t++){
    		v_panel.get(t).activated();
    	}
	}

	public void deactivate() {
    	for (int t=0;t<v_panel.size();t++){
    		v_panel.get(t).deactivated();
    	}
	}
    
}

